package cxm.lib.TestWebrtc;

import java.util.Arrays;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.android.webrtc.audio.MobileAEC;
import com.android.webrtc.audio.NoiseSuppression;
import com.android.webrtc.audio.VoiceActivityDetector;

public class MainActivity extends Activity {
	// params for recording and playing
	protected int freq = 8000;
	protected int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
	protected int channelIn = AudioFormat.CHANNEL_IN_MONO;
	protected int channelOut = AudioFormat.CHANNEL_OUT_MONO;
	private AudioRecord record = null;
	private AudioTrack track = null;
	MobileAEC aecm = null;
	NoiseSuppression ns = null;
	VoiceActivityDetector vad = null;
	private boolean isRecording = false;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
	
	int min = 0;
	int maxJitter = 0;

	public void onEchoClicked(View view) {
		if (!this.isRecording) {
			// start echo
			// new audio record
			min = AudioRecord.getMinBufferSize(this.freq,
					this.channelIn, this.audioFormat);
			this.record = new AudioRecord(MediaRecorder.AudioSource.MIC,
					this.freq, this.channelIn, this.audioFormat, min);
			maxJitter = AudioTrack.getMinBufferSize(freq,
					this.channelOut, this.audioFormat);
			this.track = new AudioTrack(AudioManager.STREAM_MUSIC, this.freq,
					this.channelOut, this.audioFormat, maxJitter,
					AudioTrack.MODE_STREAM);
			// new speex for echo cancellation
			aecm = new MobileAEC(null);
			aecm.setAecmMode(MobileAEC.AggressiveMode.AGGRESSIVE).prepare();
			try {
				ns = new NoiseSuppression(NoiseSuppression.NS_HIGH, this.freq);
				vad = new VoiceActivityDetector();
			} catch (Exception e) {
				Log.d("MainActivity", "Cannot create noise suppression " + e);
			}

			this.track.play();
			this.record.startRecording();

			this.isRecording = true;

			new RecordAudio(160).execute();
		} else {
			this.isRecording = false;
			while (null != track)
				; // waiting for track to null
		}
	}

	private class RecordAudio extends AsyncTask<Void, Integer, Void> {
		short []buffer = null;

		public RecordAudio(int len) {
			this.buffer = new short[len];
		}

		private void clean() {
			// stop record
			record.stop();
			track.stop();
			record.release();
			track.release();
			aecm.close();
			ns.close();
			vad.close();
			record = null;
			track = null;
			ns = null;
		}

		@Override
		protected Void doInBackground(Void... params) {
			short readDelay = 0;
			short writeDelay = 0;
			short processDelay = 0;
			long startTime = 0;
			while (isRecording) {
				// read audio buffer
				startTime = System.currentTimeMillis();
				int bufferReadResult = record.read(buffer, 0, buffer.length);
				readDelay = (short)(System.currentTimeMillis() - startTime);
				if (bufferReadResult == buffer.length) {
					// farend buffer
					try {
						// noise supporession
						short []cleanBuffer = new short[this.buffer.length];
						ns.process(buffer, cleanBuffer);
						
						startTime = System.currentTimeMillis();
						aecm.farendBuffer(cleanBuffer, (short)buffer.length);
						// echo cancel to new buffer
						short []echoCancelBuffer = new short[this.buffer.length];
						processDelay = (short)(System.currentTimeMillis() - startTime);
						// calculate delay
						short delayTime = (short)(readDelay + writeDelay + processDelay + ((min + maxJitter) / 160 * 20));
						Log.d("MainActivity", "Set delay " + delayTime);
						// cancel echo
						aecm.echoCancellation(buffer, null, echoCancelBuffer, (short)buffer.length, (short)120);

						// process buffer
						startTime = System.currentTimeMillis();
						// vad
						if (!vad.process(freq, echoCancelBuffer))
							Arrays.fill(echoCancelBuffer, (short)0);
						track.write(echoCancelBuffer, 0, buffer.length);
						writeDelay = (short)(System.currentTimeMillis() - startTime);
					} catch (Exception e) {
						Log.e("MainActivity", "process echo cancel failed");
					}
				}
				else
					Log.e("MainActivity", "Read buffer size unmatch");
			}

			this.clean();

			return null;
		}
	}
}
